<template>
  <div class="vk-VCode">
    <canvas
      :id="canvasId"
      :canvas-id="canvasId"
      :width="width"
      :height="height"
      v-if="canvasId"
    ></canvas>
  </div>
</template>

<script>
// Math.ceil()向上取整，
// Math.random() 生成一个0到1之间的随机小数。
// Math.ceil(Math.random() * 10e5)是生成一个介于 1 到 100,000（包括 100,000）之间的随机整数。这个表达式常用于需要生成唯一标识符、验证码等场景。
// toString(36)支持从 2 到 36 的进制，进制 36 包含了 0-9 和 a-z 这 36 个字符，因此可以生成包含字母和数字的字符串。
const canvasId = `vk_${Math.ceil(Math.random() * 10e5).toString(36)}`;

// 获取范围内的随机数
const getRandomNum = (min, max) => {
  return Math.floor(Math.random() * (max - min + 1) + min);
};

// 获取范围内的随机颜色
const getRandomColor = () => {
  return `rgb(${getRandomNum(0, 255)},${getRandomNum(0, 255)},${getRandomNum(
    0,
    255
  )})`;
};

export default {
  name: "VkVCode",
  emits: ["change"],
  props: {
    //验证码范围字符串
    vcodeStr: {
      type: String,
      default: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
    },
    //验证码长度
    length: {
      type: [Number, String],
      default: 4,
    },
    //画布宽度
    width: {
      type: [Number, String],
      default: 100,
    },
    //画布高度
    height: {
      type: [Number, String],
      default: 36,
    },
    //画布背景色
    bgColor: {
      type: String,
      default: "pink",
    },
    //画布字体颜色
    fontColor: {
      type: String,
      default: "",
    },
    //是否显示干扰点
    hasPoint: {
      type: Boolean,
      default: true,
    },
    //是否显示干扰线
    hasLine: {
      type: Boolean,
      default: true,
    },
  },
  data() {
    return {
      canvasId,
      canvas: null, // 画布
      ctx: null, // 画笔
    };
  },

  created() {
    this.ctx = null;
  },
  mounted() {
    this.$nextTick(() => {
      setTimeout(() => {
        this.draw();
      }, 50);
    });
  },
  beforeDestroy() {
    this.ctx = null;
    this.canvas = null;
  },
  methods: {
    draw() {
      // 获取画布
      this.canvas = document.getElementById(this.canvasId);
      // 获取画笔
      this.ctx = this.canvas.getContext("2d");
      // 清除画布上所有内容
      this.ctx.clearRect(0, 0, this.width, this.height);
      const value = this.render(this.ctx);
      this.$emit("change", value);
    },

    render(ctx) {
      let vcode = ""; // 验证码
      ctx.textBaseline = "bottom";
      let width = Number(this.width);
      let height = Number(this.height);

      // 填充颜色
      ctx.fillStyle = this.bgColor ? this.bgColor : getRandomColor(180, 240);
      // 绘制矩形
      ctx.fillRect(0, 0, width, height);

      let num = Number(this.length) || 4;
      for (let i = 0; i < num; i++) {
        const x = ((width - 10) / num) * i + 10;
        const y = getRandomNum(height / 2, height);
        const deg = getRandomNum(-45, 45); // 旋转角度
        // 随机获取验证码
        const txt = this.vcodeStr[getRandomNum(0, this.vcodeStr.length)];
        const fontSize = getRandomNum(16, 40);
        const halfHeight = parseInt(height / 2);

        vcode += txt;

        ctx.fillStyle = this.fontColor ? this.fontColor : getRandomColor();
        ctx.fontSize = fontSize > halfHeight ? halfHeight : fontSize;
        ctx.translate(x, y);
        ctx.rotate((deg * Math.PI) / 180);
        ctx.fillText(txt, 0, 0);
        ctx.rotate((-deg * Math.PI) / 180);
        ctx.translate(-x, -y);
      }

      // 绘制干扰点
      if (!!this.hasPoint) {
        for (let i = 0; i < 10; i++) {
          ctx.fillStyle = getRandomColor(0, 255);
          ctx.beginPath(); // 重置路径
          // 绘制弧形
          ctx.arc(
            getRandomNum(0, width),
            getRandomNum(0, height),
            1,
            0,
            2 * Math.PI
          );
          ctx.fill(); // 填充绘制当前路径。
        }
      }

      // 绘制干扰线
      if (!!this.hasLine) {
        for (let i = 0; i < 4; i++) {
          ctx.strokeStyle = getRandomColor(0, 255);
          ctx.beginPath();
          ctx.moveTo(getRandomNum(0, width), getRandomNum(0, height));
          ctx.lineTo(getRandomNum(0, width), getRandomNum(0, height));
          ctx.stroke();
        }
      }

      return vcode;
    },
  },
};
</script>

<style scoped></style>
